﻿using Crux.Core.Validations;

namespace System;
/// <summary>
/// 
/// </summary>
public static class Requires
{
    #region public static method
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static T NotNull<T>(T value, string parameterName = "")
    {
        if (value is null)
        {
            throw new ArgumentNullException(parameterName);
        }
        return value;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static IntPtr NotNull(IntPtr value, string parameterName = "")
    {
        if (value == IntPtr.Zero)
        {
            throw new ArgumentNullException(parameterName);
        }
        return value;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    [DebuggerStepThrough]
    public static void NotNull(Task value, string parameterName = "")
    {
        if (value is null)
        {
            throw new ArgumentNullException(parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    /// <typeparam name="T"></typeparam>
    [DebuggerStepThrough]
    public static void NotNull<T>(Task<T> value, string parameterName = "")
    {
        if (value is null)
        {
            throw new ArgumentNullException(parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static T NotNullAllowStructs<T>(T value, string parameterName = "")
    {
        if (value is null)
        {
            throw new ArgumentNullException(parameterName);
        }
        return value;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    [DebuggerStepThrough]
    public static void NotNullOrEmpty(string value, string parameterName = "")
    {
        if (value is null)
        {
            throw new ArgumentNullException(parameterName);
        }
        if (value.Length == 0 || value[0] == '\0')
        {
            throw new ArgumentException(Format(Strings.Argument_EmptyString, parameterName), parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    [DebuggerStepThrough]
    public static void NotNullOrWhiteSpace(string value, string parameterName = "")
    {
        if (value is null)
        {
            throw new ArgumentNullException(parameterName);
        }
        if (value.Length == 0 || value[0] == '\0')
        {
            throw new ArgumentException(Format(Strings.Argument_EmptyString, parameterName), parameterName);
        }
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentException(Format(Strings.Argument_Whitespace, parameterName));
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="values"></param>
    /// <param name="parameterName"></param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <exception cref="ArgumentException"></exception>
    [DebuggerStepThrough]
    public static void NotNullOrEmpty(IEnumerable values, string parameterName = "")
    {
        if (values is null)
        {
            throw new ArgumentNullException(parameterName);
        }
        ICollection? collection = values as ICollection;
        if (collection is null)
        {
            IEnumerator enumerator = values.GetEnumerator();
            using (enumerator as IDisposable)
            {
                if (enumerator.MoveNext())
                {
                    return;
                }
            }
            throw new ArgumentException(Format(Strings.Argument_EmptyArray, parameterName), parameterName);
        }
        if (collection!.Count > 0)
        {
            return;
        }
        throw new ArgumentException(Format(Strings.Argument_EmptyArray, parameterName), parameterName);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="values"></param>
    /// <param name="parameterName"></param>
    /// <typeparam name="T"></typeparam>
    [DebuggerStepThrough]
    public static void NotNullEmptyOrNullElements<T>(IEnumerable<T> values, string parameterName = "")
        where T : class
    {
        NotNull(values, parameterName);
        bool hasElements = false;
        foreach (T value in values)
        {
            hasElements = true;
            if (value is null)
            {
                throw new ArgumentException(Format(Strings.Argument_NullElement, parameterName), parameterName);
            }
        }
        if (!hasElements)
        {
            throw new ArgumentException(Format(Strings.Argument_EmptyArray, parameterName), parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="values"></param>
    /// <param name="parameterName"></param>
    /// <typeparam name="T"></typeparam>
    [DebuggerStepThrough]
    public static void NullOrNotNullElements<T>(IEnumerable<T> values, string parameterName = "")
    {
        if (values is not null)
        {
            foreach (T value in values)
            {
                if (value is null)
                {
                    throw new ArgumentException(Format(Strings.Argument_NullElement, parameterName), parameterName);
                }
            }
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="condition"></param>
    /// <param name="parameterName"></param>
    /// <param name="message"></param>
    [DebuggerStepThrough]
    public static void Range(bool condition, string parameterName = "", string message = "")
    {
        if (!condition)
        {
            FailRange(parameterName, message);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="parameterName"></param>
    /// <param name="message"></param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static Exception FailRange(string parameterName = "", string message = "")
    {
        if (string.IsNullOrEmpty(message))
        {
            throw new ArgumentOutOfRangeException(parameterName);
        }
        else
        {
            throw new ArgumentOutOfRangeException(parameterName, message);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="value"></param>
    /// <param name="parameterName"></param>
    /// <typeparam name="T"></typeparam>
    [DebuggerStepThrough]
    public static void Defined<T>(T value, string parameterName)
        where T : struct, IConvertible, IComparable, IFormattable
    {
        if (!Enum.IsDefined(typeof(T), value))
        {
            throw new ArgumentException(Format(Strings.Argument_EnumNotDefined, parameterName, typeof(T).FullName!), parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="condition"></param>
    /// <param name="parameterName"></param>
    /// <param name="message"></param>
    [DebuggerStepThrough]
    public static void Argument(bool condition, string parameterName, string message)
    {
        if (!condition)
        {
            throw new ArgumentException(message, parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="condition"></param>
    /// <param name="parameterName"></param>
    /// <param name="message"></param>
    /// <param name="arg1"></param>
    [DebuggerStepThrough]
    public static void Argument(bool condition, string parameterName, string message, object arg1)
    {
        if (!condition)
        {
            throw new ArgumentException(Format(message, arg1), parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="condition"></param>
    /// <param name="parameterName"></param>
    /// <param name="message"></param>
    /// <param name="arg1"></param>
    /// <param name="arg2"></param>
    [DebuggerStepThrough]
    public static void Argument(bool condition, string parameterName, string message, object arg1, object arg2)
    {
        if (!condition)
        {
            throw new ArgumentException(Format(message, arg1, arg2), parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="condition"></param>
    /// <param name="parameterName"></param>
    /// <param name="message"></param>
    /// <param name="args"></param>
    [DebuggerStepThrough]
    public static void Argument(bool condition, string parameterName, string message, params object[] args)
    {
        if (!condition)
        {
            throw new ArgumentException(Format(message, args), parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="condition"></param>
    /// <param name="parameterName"></param>
    /// <param name="unformattedMessage"></param>
    /// <param name="args"></param>
    [DebuggerStepThrough]
    public static void That(bool condition, string parameterName, string unformattedMessage, params object[] args)
    {
        if (!condition)
        {
            throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args), parameterName);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="condition"></param>
    /// <param name="message"></param>
    [DebuggerStepThrough]
    public static void ValidState(bool condition, string message)
    {
        if (!condition)
        {
            throw new InvalidOperationException(message);
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static Exception Fail(string message) => throw new ArgumentException(message);
    /// <summary>
    /// 
    /// </summary>
    /// <param name="unformattedMessage"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static Exception Fail(string unformattedMessage, params object[] args) => throw Fail(Format(unformattedMessage, args));
    /// <summary>
    /// 
    /// </summary>
    /// <param name="innerException"></param>
    /// <param name="unformattedMessage"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static Exception Fail(Exception innerException, string unformattedMessage, params object[] args) => throw new ArgumentException(Format(unformattedMessage, args), innerException);
    #endregion
    #region private static method
    private static string Format(string format, params object[] arguments) => PrivateErrorHelpers.Format(format, arguments);
    #endregion
}
